package com.kamistoat.meimeichat.meimeichatserverapplication.netty.utils;

import com.kamistoat.meimeichatserveraggrement.protocol.login.dto.ClientAddressDto;
import io.netty.channel.Channel;
import io.netty.channel.ChannelId;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.util.concurrent.GlobalEventExecutor;

import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 提供一些Util方法
 */
public class NettySocketChannelUtil {

    // TODO 暂时还没有用缓存来做，存在线程危险，暂时使用ConcurrentHashMap处理线程风险
    /**
     * 用户Map. 每一个用户Id对应一个 Channel
     */
    public static final Map<String, Channel> USER_ID_CHANNEL_MAP = new ConcurrentHashMap<>();

    /**
     * 用户ChannelId-用户Id 的Map。可用于根据ChannelId删除用户Channel
     */
    public static final Map<String, String> CHANNEL_ID_USER_ID_MAP = new ConcurrentHashMap<>();

    /**
     * 群组Map。每一个群组Id对应一个 ChannelGroup，保存该群组所有在线成员的channel
     */
    public static final Map<String, ChannelGroup> GROUP_ID_CHANNEL_MAP = new ConcurrentHashMap<>();

    public static final Map<String, InetSocketAddress> USER_ID_P2P_ADDRESS_MAP = new ConcurrentHashMap<>();

    /**
     * 将在线用户的Channel添加到两个Map中
     *
     * @param userId
     * @param channel
     */
    public static void addChannel(String userId, Channel channel) {
        USER_ID_CHANNEL_MAP.put(userId, channel);
        CHANNEL_ID_USER_ID_MAP.put(channel.id().toString(), userId);
    }

    /**
     * 根据用户ChannelId查询其userId
     * @param channelId
     * @return
     */
    public static String getUserIdByChannelId(String channelId){
        return CHANNEL_ID_USER_ID_MAP.get(channelId);
    }

    /**
     * 根据ChannelId从两个Map中删除对应的用户Channel
     *
     * @param channelId
     */
    public static void removeChannel(String channelId) {
        String userId = CHANNEL_ID_USER_ID_MAP.get(channelId);
        if (null == userId) {
            return;
        }
        USER_ID_CHANNEL_MAP.remove(userId);
        CHANNEL_ID_USER_ID_MAP.remove(channelId);
    }

    /**
     * 根据用户Id从两个Map中删除用户的Channel
     *
     * @param userId
     */
    public static void removeUserChannelByUserId(String userId) {
        Channel channel = USER_ID_CHANNEL_MAP.get(userId);
        if (channel == null) {
            return;
        }
        String channelId = channel.id().toString();
        CHANNEL_ID_USER_ID_MAP.remove(channelId);
        USER_ID_CHANNEL_MAP.remove(userId);
    }

    /**
     * 根据UserId寻找对应的Channel
     *
     * @param userId
     * @return
     */
    public static Channel getChannel(String userId) {
        return USER_ID_CHANNEL_MAP.get(userId);
    }

    /**
     * 将一个群成员的userChannel添加到该群组的群组Channel中
     *
     * @param groupId     对话框ID[群Id号]
     * @param userChannel 群员通信管道
     */
    public synchronized static void addChannelGroup(String groupId, Channel userChannel) {
        ChannelGroup channelGroup = GROUP_ID_CHANNEL_MAP.get(groupId);
        // 若为null，则说明当前群组没有任何其他成员在线(成员下线会从群组channel中删除自身，当最后一个成员下线时同时删除群组channel)，当前用户为唯一在线成员。
        // 则先创建群组channel并添加自身
        if (null == channelGroup) {
            channelGroup = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
            GROUP_ID_CHANNEL_MAP.put(groupId, channelGroup);
        }
        channelGroup.add(userChannel);
    }

    /**
     * 根据groupId获取该群组的ChannelGroup
     *
     * @param groupId 群Id
     * @return ChannelGroup
     */
    public static ChannelGroup getChannelGroup(String groupId) {
        return GROUP_ID_CHANNEL_MAP.get(groupId);
    }

    /**
     * 将一个群成员的userChannel从指定的群组的ChannelGroup中删除
     * 该方法 synchronized
     *
     * @param groupId
     * @param userChannel
     */
    public synchronized static void removeChannelGroup(String groupId, Channel userChannel) {
        ChannelGroup channelGroup = GROUP_ID_CHANNEL_MAP.get(groupId);
        if (null == channelGroup) return;
        channelGroup.remove(userChannel);
        // TODO 当群组在线成员为空时，应该删除 GroupChannel。注意现在的实现存在线程风险
        if (channelGroup.size() == 0) {
            GROUP_ID_CHANNEL_MAP.remove(groupId);
        }

    }

    /**
     * 给定一个userChannel。遍历所有的群组ChannelGroup，将其从中删除，可用于用户下线时从所有群组中踢出。
     * 未做群组区分，效率较低，建议还是先查询用户所在groupIds，然后调用上一个方法
     *
     * @param userChannel
     */
    public static void removeChannelGroupByChannel(Channel userChannel) {
        Set<String> allGroupIds = GROUP_ID_CHANNEL_MAP.keySet();
        for (String groupId : allGroupIds) {
            removeChannelGroup(groupId, userChannel);
        }
    }


    /**
     * 给定一个userId，将其对应的userChannel从指定的群组ChannelGroup中踢出
     */
    public static void removeChannelGroupByUserId(String groupId, String userId) {
        Channel userChannel = USER_ID_CHANNEL_MAP.get(userId);
        removeChannelGroup(groupId, userChannel);
    }
}
